#include "myglStringControl.h"
#pragma comment (lib, "gdiplus.lib")

#define CUR_LINE ((h - y) * fontBitmap.Stride)
#define CUR_POS (y * w + x)


myglStringControl::myglStringControl (const WCHAR* strFontFamily,
                                      UINT fontSize,
                                      Ptr< ShowListManager > pShowListMng)
{

  _pShowListMng = pShowListMng;

  ULONG gdipToken;
  GdiplusStartupInput gdipSIn;
  GdiplusStartup (&gdipToken, &gdipSIn, NULL);

  _create_character_list (strFontFamily, fontSize);

  GdiplusShutdown (gdipToken);

}


myglStringControl::~myglStringControl(void)
{
  glDeleteTextures (128, _texName);
  for (UINT i = 0; i < 128; ++i)
    _pShowListMng->deleteList (_showListName[i]);
}

void myglStringControl::DrawString (const char* str, GLfloat height,
                                    AlignMode mode, float charSpacePercent,
                                    UINT length, GLfloat hwRatio)
{
  glPushMatrix ();

  float charSpaceActual = charSpacePercent * (float)_fontHeight;

  // Calculate width of string
  vector< float > strWidth;

  float strWidthCurLine = 0.0f;

  for (UINT i = 0; i < length && str[i] != 0; ++i)
  {
    if (str[i] == '\n')
    {
      strWidth.push_back (strWidthCurLine);
      strWidthCurLine = 0;
    }
    else
      strWidthCurLine += (float)_fontWidth[str[i]] + charSpaceActual;
  }
  strWidth.push_back (strWidthCurLine);

  glScalef (height / _fontHeight, height / _fontHeight * hwRatio, 1);

  glPushMatrix ();
  switch (mode)
  {
  case ALIGN_RIGHT:
    glTranslatef (-strWidth[0], 0.0f, 0.0f); break;
  case ALIGN_MIDDLE:
    glTranslatef (-strWidth[0] / 2.0f, 0.0f, 0.0f); break;
  }
  int line = 0;
  for (UINT i = 0; i < length && str[i] != 0; ++i)
  {
    if (str[i] == '\n')
    {
      glPopMatrix ();
      ++line;
      glTranslatef (0.0f, -(int)_fontHeight, 0.0f);
      glPushMatrix ();
      switch (mode)
      {
      case ALIGN_RIGHT:
        glTranslatef (-strWidth[line], 0.0f, 0.0f);
        break;
      case ALIGN_MIDDLE:
        glTranslatef (-strWidth[line] / 2.0f, 0.0f, 0.0f);
        break;
      }
    }
    else
    {
      if (glIsList (_showListName[str[i]]))
        glCallList (_showListName[str[i]]);
      if (charSpaceActual != 0)
        glTranslatef (charSpaceActual, 0, 0);
    }
  }
  glPopMatrix ();
  glPopMatrix ();
}


void myglStringControl::_bind_tex_and_draw (char ch, BitmapData& fontBitmap)
{

  UINT w = fontBitmap.Width;
  UINT h = fontBitmap.Height;
  _fontWidth[ch] = w;

  const UINT R = 0;
  const UINT G = 1;
  const UINT B = 2;
  const UINT A = 3;

  BYTE* texData = new BYTE[w * h * 4];
  BYTE* origData = (BYTE*)fontBitmap.Scan0;

  // Copy and convert image data
  for (UINT y = 0; y < h; ++y)
  {
    for (UINT x = 0; x < w; ++x)
    {
      if (origData[CUR_LINE + x * 3] != 255)
      {
        texData[CUR_POS * 4 + R] = 255;
        texData[CUR_POS * 4 + G] = 255;
        texData[CUR_POS * 4 + B] = 255;
      }
      else
      {
        texData[CUR_POS * 4 + R] = 0;
        texData[CUR_POS * 4 + G] = 0;
        texData[CUR_POS * 4 + B] = 0;
      }
      texData[CUR_POS * 4 + A] = 255 - origData[CUR_LINE + x * 3];
    }
  }

  // Create a texture
  glGenTextures(1, &_texName[ch]);

  glBindTexture(GL_TEXTURE_2D, _texName[ch]);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glTexImage2D(GL_TEXTURE_2D,
    0, // Level
    4,
    w, h,
    0, // Border width
    GL_RGBA,
    GL_UNSIGNED_BYTE,
    texData
    );

  delete[] texData;
  glBindTexture (GL_TEXTURE_2D, 0);

  // Create a Show List
  _showListName[ch] = _pShowListMng->createNewList (GL_COMPILE);
  glPushAttrib (GL_ENABLE_BIT);
  glEnable (GL_TEXTURE_2D);
  glPushAttrib (GL_TEXTURE_BIT);
  glBindTexture (GL_TEXTURE_2D, _texName[ch]);

  glBegin (GL_QUADS);
  glNormal3i (0, 0, 1);
  glTexCoord2f (0, 0); glVertex2i (0, 0);
  glTexCoord2f (1, 0); glVertex2i (w, 0);
  glTexCoord2f (1, 1); glVertex2i (w, h);
  glTexCoord2f (0, 1); glVertex2i (0, h);
  glEnd ();
  
  glPopAttrib ();
  glPopAttrib ();
  glTranslatef (w, 0, 0); // Set to Next Character's Position
  glEndList ();
}

void myglStringControl::_create_character_list (const WCHAR* strFontFamily,
                                                UINT fontSize)
{

  // Initialize Devices
  Ptr< Bitmap > pCharBitmap = new Bitmap (2 * fontSize, 2 * fontSize,
    PixelFormat24bppRGB);
  Graphics g (pCharBitmap);

  Ptr< const FontFamily > pFontFamily = new FontFamily (strFontFamily);

  if (!pFontFamily->IsAvailable ()) // Font specified not found
    pFontFamily = FontFamily::GenericSansSerif ();

  Ptr< Font > pFont = new Font (pFontFamily, fontSize);

  SolidBrush fontBrush (Color (0, 0, 0));

  // Create bitmaps for every characters
  for (char ch = 0; ch >= 0; ++ch)
  {
    g.Clear (Color (255, 255, 255));

    RectF necessaryAreaF;
    WCHAR wch = ch;

    // Get the width and height of the character
    g.MeasureString (&wch, 1, pFont, PointF (0, 0), &necessaryAreaF);
    _fontHeight = necessaryAreaF.Height;

    g.DrawString (&wch, 1, pFont, PointF (0, 0), &fontBrush);

    // Convert RectF to Rect
    Rect necessaryArea (necessaryAreaF.X, necessaryAreaF.Y, 
      necessaryAreaF.Width, necessaryAreaF.Height);

    // Get source data
    BitmapData bitmapData;
    pCharBitmap->LockBits (&necessaryArea, ImageLockModeWrite,
      PixelFormat24bppRGB, &bitmapData);
    _bind_tex_and_draw (ch, bitmapData);
    pCharBitmap->UnlockBits (&bitmapData);
  }

}